package com.example.clientmiviamoviego4;

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

/**
 * Activity che gestisce la parte di dettaglio di un film selezionato usando
 * un'animazione a rotazione di pagina (la cosidetta "card flip"). Si può
 * passare da una schermata dedicata alle informazioni salienti riguardanti il
 * film selezionato, ad un'altra dedicata al trailer del film stesso premendo un
 * pulsante ben visibile in alto a destra, che attiva la rotazione della
 * schermata.
 * 
 * @author Gang of Four
 * 
 */

public class CardFlipActivity extends Activity implements
		FragmentManager.OnBackStackChangedListener {
	/**
	 * Oggetto utilizzato per gestire le operazioni UI.
	 * */
	private Handler mHandler = new Handler();
	/**
	 * Booleano indicante la visualizzazione della schermata frontale o della
	 * schermata posteriore.
	 * */
	private boolean mShowingBack = false;
	/**
	 * Stringa rappresentante l'url relativo all'indirizzo IP del server
	 * utilizzato.
	 * */
	private String basePath;
	/**
	 * Oggetto che raccoglie le informazioni dettagliate relative al film
	 * selezionato.
	 * */
	private ParcelableDetailedMovie detailedMovie;
	/**
	 * Campo relativo al nome dell'item in cui è memorizzato l'ID del film
	 * selezionato.
	 * */
	public static final String SELECTED_MOVIE_ID = "selectedMovieID";
	/**
	 * Campo relativo al nome dell'item in cui sono memorizzate le informazioni
	 * dettagliate del film selezionato.
	 * */
	public static final String DETAILED_MOVIE = "detailedMovie";
	/**
	 * Campo relativo al nome dell'item in cui è memorizzata l'URL del film
	 * selezionato.
	 * */
	public static final String TRAILER_URL = "trailerURL";
	/**
	 * Codice della risposta HTTP ricevuta dal server.
	 * */
	private Integer result;

	/**
	 * Metodo richiamato quando l'activity viene creata per la prima volta.
	 * Gestisce lo scambio di informazioni tra l'activity o il fragment che ha
	 * richiamato la CardFlipActivity, e lo scambio di informazioni tra la
	 * CardFlipActivity stessa e le inner class CardFrontFragment e
	 * CardBackFragment.
	 * 
	 * */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_card_flip);

		Intent intent = getIntent();
		basePath = intent.getStringExtra("basePath");
		String movieID = intent.getStringExtra(SELECTED_MOVIE_ID);

		String title = intent.getStringExtra("title");
		ActionBar actionBar = getActionBar();
		actionBar.setTitle(title);

		try {
			result = new LongOperation(movieID).execute().get();

			if (result == 404) {
				Toast toast = Toast.makeText(this,
						getResources().getString(R.string.server_error),
						Toast.LENGTH_SHORT);
				toast.show();
				finish();
			}
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}

		Bundle frontFragmentBundle = new Bundle();
		frontFragmentBundle.putParcelable(DETAILED_MOVIE, detailedMovie);

		if (savedInstanceState == null) {
			CardFrontFragment frontFragment = new CardFrontFragment();
			frontFragment.setArguments(frontFragmentBundle);

			getFragmentManager().beginTransaction()
					.add(R.id.container, frontFragment).commit();
		} else {
			mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
		}

		getFragmentManager().addOnBackStackChangedListener(this);
	}

	/**
	 * Metodo che viene richiamato all'atto della creazione dei pulsanti in alto
	 * a destra, i quali consentono di passare da una scheramata all'altra della
	 * CardFlipActivity. Tali pulsanti devono essere aggiornati a seconda della
	 * schermata visualizzata.
	 * 
	 * */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		MenuItem item = menu.add(Menu.NONE, R.id.action_flip, Menu.NONE,
				mShowingBack ? R.string.action_detail : R.string.action_play);
		item.setIcon(mShowingBack ? R.drawable.ic_action_detail
				: R.drawable.ic_action_play);
		item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		return true;
	}

	/**
	 * Metodo che viene richiamato all'atto della pressione del pulsante in alto
	 * a destra.
	 * 
	 * */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			finish();
			return true;

		case R.id.action_flip:
			flipCard();
			return true;
		}

		return super.onOptionsItemSelected(item);
	}

	/**
	 * Metodo che attiva la rotazione dalla schermata principale alla
	 * secondaria, e viceversa.
	 * */
	private void flipCard() {
		if (mShowingBack) {
			getFragmentManager().popBackStack();
			return;
		}

		mShowingBack = true;

		Bundle backFragmentBundle = new Bundle();
		backFragmentBundle
				.putString(TRAILER_URL, detailedMovie.getTrailerURL());
		CardBackFragment backFragment = new CardBackFragment();
		backFragment.setArguments(backFragmentBundle);

		getFragmentManager()
				.beginTransaction()

				.setCustomAnimations(R.animator.card_flip_right_in,
						R.animator.card_flip_right_out,
						R.animator.card_flip_left_in,
						R.animator.card_flip_left_out)

				.replace(R.id.container, backFragment)

				.addToBackStack(null)

				.commit();

		mHandler.post(new Runnable() {
			@Override
			public void run() {
				invalidateOptionsMenu();
			}
		});
	}

	/**
	 * Metodo richiamato quando il contenuto del back stack cambia.
	 * */
	@Override
	public void onBackStackChanged() {
		mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);

		invalidateOptionsMenu();
	}

	/**
	 * Classe che gestisce la connessione al server per richiedere le
	 * informazioni dettagliate riguardanti il film selezionato.
	 * 
	 * */
	private class LongOperation extends AsyncTask<String, Void, Integer> {
		/**
		 * Dialog visualizzato in attesa del compimento delle operazioni in
		 * background.
		 * */
		private ProgressDialog dialog = new ProgressDialog(
				CardFlipActivity.this);
		/**
		 * ID relativo al film selezionato.
		 * */
		private String movieId;
		/**
		 * Codice della risposta HTTP ricevuta dal server.
		 * */
		private int errorMessage;

		/**
		 * Costruttore della classe LongOperation.
		 * */
		public LongOperation(String id) {
			movieId = id;
		}

		/**
		 * Metodo che viene richiamato prima della chiamata a
		 * doInBackground(Params...)
		 * */
		protected void onPreExecute() {
			super.onPreExecute();
			dialog.setMessage(getResources().getString(R.string.wait));
			dialog.show();
			dialog.setCancelable(false);
		}

		/**
		 * Metodo che fa la chiamata al server in background, e attende la
		 * risposta
		 * */
		protected Integer doInBackground(String... urls) {

			HandleConnection connectionRequest = new HandleConnection(basePath,
					"MiviaMovieServer/rest/movies/" + movieId,
					OperationEnum.GET_MOVIE_FROM_ID);

			errorMessage = connectionRequest.doGet();

			
			detailedMovie = ((ArrayList<ParcelableDetailedMovie>) connectionRequest
					.getArrayList()).get(0);

			return errorMessage;
		}

		/**
		 * Metodo che viene richiamato dopo la chiamata a
		 * doInBackground(Params...)
		 * */
		protected void onPostExecute(Integer unused) {
			dialog.dismiss();
		}
	}

	/**
	 * Classe rappresentante il fragment relativo alla schermata principale.
	 */
	public static class CardFrontFragment extends Fragment {
		/**
		 * Parametro indicante la durata dell'animazione di passaggio da una
		 * schermata all'altra.
		 */
		private int mShortAnimationDuration;
		/**
		 * Riferimento all'oggetto Animator corrente.
		 */
		private Animator mCurrentAnimator;
		/**
		 * Riferimento al layout caricato
		 * */
		private View root;
		/**
		 * Campo relativo al nome dell'item in cui sono memorizzate le
		 * informazioni dettagliate del film selezionato.
		 * */
		public static final String DETAILED_MOVIE = "detailedMovie";
		/**
		 * Oggetto relativo alle informazioni dettagliate riguardanti il film
		 * selezionato.
		 * */
		private ParcelableDetailedMovie detailedMovie;
		/**
		 * TextView relative a titolo, regista e data di uscita del film
		 * selezionato.
		 * */
		private TextView title, director, date;
		/**
		 * Riferimento all'ogetto ImageLoader che si occuperà del caricamento
		 * delle immagini.
		 * */
		private ImageLoader imageLoader;
		/**
		 * Riferimento all'ogetto TouchHighlightImageButton che si occuperà
		 * delle immagini di visualizzare all'interno del button sul quale si
		 * potrà cliccare per fare lo zoom dell'immagine.
		 * */
		private TouchHighlightImageButton thumbnailButton;

		/**
		 * Costruttore della classe CardFrontFragment.
		 * */
		public CardFrontFragment() {
		}

		/**
		 * Metodo richiamato quando la gerarchia di view viene associata al
		 * fragment. Gestisce lo scambio di informazioni tra l'activity
		 * CardFlipActivity e il fragment CardFrontFragment, e la
		 * visualizzazione delle informazioni a schermo.
		 * */
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			detailedMovie = getArguments().getParcelable(DETAILED_MOVIE);

			root = inflater.inflate(R.layout.fragment_card_front, container,
					false);

			thumbnailButton = (TouchHighlightImageButton) root
					.findViewById(R.id.thumbnailButton);
			title = (TextView) root.findViewById(R.id.titleDetailedText);
			director = (TextView) root.findViewById(R.id.directorDetailedText);
			date = (TextView) root.findViewById(R.id.dateDetailedText);

			imageLoader = new ImageLoader(getActivity());
			imageLoader.DisplayImage(detailedMovie.getThumbnailURL(),
					thumbnailButton);
			title.setText(detailedMovie.getTitle());
			director.setText(detailedMovie.getDirector());
			date.setText(detailedMovie.getDate());

			LinearLayout actorsDetailedInfo = (LinearLayout) root
					.findViewById(R.id.actorsDetailedInfoContainer);

			for (String actor : detailedMovie.getActors()) {
				TextView actorTextView = new TextView(getActivity());
				actorTextView.setLayoutParams(new LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
				actorTextView.setText("- " + actor);
				actorsDetailedInfo.addView(actorTextView);
			}

			LinearLayout genresDetailedInfo = (LinearLayout) root
					.findViewById(R.id.genresDetailedInfoContainer);

			for (ParcelableMovieType movieType : detailedMovie.getMovieTypes()) {
				TextView actorTextView = new TextView(getActivity());
				actorTextView.setLayoutParams(new LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
				actorTextView.setText("- " + movieType.getKey());
				genresDetailedInfo.addView(actorTextView);
			}

			thumbnailButton.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View view) {
					zoomImageFromThumb(thumbnailButton, R.id.playbillImage);
				}
			});

			// Retrieve and cache the system's default "short" animation time.
			mShortAnimationDuration = getResources().getInteger(
					android.R.integer.config_shortAnimTime);

			return root;
		}

		/**
		 * Metodo che gestisce l'animazione relativa allo zoom dalla miniatura
		 * alla locandina del film, e viceversa.
		 * 
		 * @param thumbView
		 *            La miniatura da zoommare.
		 * @param imageResId
		 *            ID relativo alla risorsa che si occupera' di visualizzare
		 *            la locandina.
		 */
		private void zoomImageFromThumb(final View thumbView, int imageResId) {

			if (mCurrentAnimator != null) {
				mCurrentAnimator.cancel();
			}

			final ImageView expandedImageView = (ImageView) root
					.findViewById(R.id.playbillImage);
			imageLoader.DisplayImage(detailedMovie.getPlaybillURL(),
					expandedImageView);

			final Rect startBounds = new Rect();
			final Rect finalBounds = new Rect();
			final Point globalOffset = new Point();

			thumbView.getGlobalVisibleRect(startBounds);
			root.findViewById(R.id.container).getGlobalVisibleRect(finalBounds,
					globalOffset);
			startBounds.offset(-globalOffset.x, -globalOffset.y);
			finalBounds.offset(-globalOffset.x, -globalOffset.y);

			float startScale;
			if ((float) finalBounds.width() / finalBounds.height() > (float) startBounds
					.width() / startBounds.height()) {
				startScale = (float) startBounds.height()
						/ finalBounds.height();
				float startWidth = startScale * finalBounds.width();
				float deltaWidth = (startWidth - startBounds.width()) / 2;
				startBounds.left -= deltaWidth;
				startBounds.right += deltaWidth;
			} else {
				startScale = (float) startBounds.width() / finalBounds.width();
				float startHeight = startScale * finalBounds.height();
				float deltaHeight = (startHeight - startBounds.height()) / 2;
				startBounds.top -= deltaHeight;
				startBounds.bottom += deltaHeight;
			}

			thumbView.setAlpha(0f);
			expandedImageView.setVisibility(View.VISIBLE);

			expandedImageView.setPivotX(0f);
			expandedImageView.setPivotY(0f);

			AnimatorSet set = new AnimatorSet();
			set.play(
					ObjectAnimator.ofFloat(expandedImageView, View.X,
							startBounds.left, finalBounds.left))
					.with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
							startBounds.top, finalBounds.top))
					.with(ObjectAnimator.ofFloat(expandedImageView,
							View.SCALE_X, startScale, 1f))
					.with(ObjectAnimator.ofFloat(expandedImageView,
							View.SCALE_Y, startScale, 1f));
			set.setDuration(mShortAnimationDuration);
			set.setInterpolator(new DecelerateInterpolator());
			set.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationEnd(Animator animation) {
					mCurrentAnimator = null;
				}

				@Override
				public void onAnimationCancel(Animator animation) {
					mCurrentAnimator = null;
				}
			});
			set.start();
			mCurrentAnimator = set;

			final float startScaleFinal = startScale;
			expandedImageView.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View view) {
					if (mCurrentAnimator != null) {
						mCurrentAnimator.cancel();
					}

					AnimatorSet set = new AnimatorSet();
					set.play(
							ObjectAnimator.ofFloat(expandedImageView, View.X,
									startBounds.left))
							.with(ObjectAnimator.ofFloat(expandedImageView,
									View.Y, startBounds.top))
							.with(ObjectAnimator.ofFloat(expandedImageView,
									View.SCALE_X, startScaleFinal))
							.with(ObjectAnimator.ofFloat(expandedImageView,
									View.SCALE_Y, startScaleFinal));
					set.setDuration(mShortAnimationDuration);
					set.setInterpolator(new DecelerateInterpolator());
					set.addListener(new AnimatorListenerAdapter() {
						@Override
						public void onAnimationEnd(Animator animation) {
							thumbView.setAlpha(1f);
							expandedImageView.setVisibility(View.GONE);
							mCurrentAnimator = null;
						}

						@Override
						public void onAnimationCancel(Animator animation) {
							thumbView.setAlpha(1f);
							expandedImageView.setVisibility(View.GONE);
							mCurrentAnimator = null;
						}
					});
					set.start();
					mCurrentAnimator = set;
				}
			});
		}
	}

	/**
	 * Classe rappresentante il fragment relativo alla schermata secondaria.
	 */
	public static class CardBackFragment extends Fragment {
		/**
		 * Dialog visualizzato in attesa del compimento delle operazioni in
		 * background.
		 * */
		private ProgressDialog pDialog;
		/**
		 * Riferimento all'oggetto che si occuperà della gestione del video da
		 * visualizzare.
		 * */
		private VideoView videoview;
		/**
		 * Campo relativo al nome dell'item in cui è memorizzata l'URL del film
		 * selezionato.
		 * */
		public static final String TRAILER_URL = "trailerURL";
		/**
		 * Campo relativo all'URL del film selezionato.
		 * */
		private String videoURL;

		/**
		 * Costruttore della classe CardBackFragment.
		 * */
		public CardBackFragment() {
		}

		/**
		 * Metodo richiamato quando la gerarchia di view viene associata al
		 * fragment. Gestisce lo scambio di informazioni tra l'activity
		 * CardFlipActivity e il fragment CardBackFragment, e la visualizzazione
		 * delle informazioni a schermo.
		 * */
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			videoURL = getArguments().getString(TRAILER_URL);

			View root = inflater.inflate(R.layout.fragment_card_back,
					container, false);

			videoview = (VideoView) root.findViewById(R.id.VideoView);

			pDialog = new ProgressDialog(getActivity());

			pDialog.setMessage(getResources().getString(R.string.buffering));
			pDialog.setIndeterminate(false);
			pDialog.show();

			try {
				MediaController mediacontroller = new MediaController(
						getActivity());
				mediacontroller.setAnchorView(videoview);
				Uri video = Uri.parse(videoURL);
				videoview.setMediaController(mediacontroller);
				videoview.setVideoURI(video);
				videoview.isPlaying();
				videoview.setOnErrorListener(new MediaPlayer.OnErrorListener() {
					public boolean onError(MediaPlayer paramMediaPlayer,
							int paramInt1, int paramInt2) {
						pDialog.dismiss();
						Toast.makeText(
								getActivity(),
								getResources().getString(
										R.string.impossible_play_video),
								Toast.LENGTH_SHORT).show();
						;
						return true;
					}
				});
				videoview.getCurrentPosition();

			} catch (Exception e) {
				Log.e("Error", e.getMessage());
				e.printStackTrace();
			}

			videoview.requestFocus();
			videoview.setOnPreparedListener(new OnPreparedListener() {
				public void onPrepared(MediaPlayer mp) {
					pDialog.dismiss();
					videoview.start();
				}
			});
			return root;
		}
	}
}
